package com.zenithmind.library.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zenithmind.common.result.PageResult;
import com.zenithmind.common.result.Result;
import com.zenithmind.common.result.ResultCode;
import com.zenithmind.library.enums.ReservationStatus;
import com.zenithmind.library.mapper.BookMapper;
import com.zenithmind.library.mapper.ReservationMapper;
import com.zenithmind.library.pojo.domain.Book;
import com.zenithmind.library.pojo.domain.Reservation;
import com.zenithmind.library.pojo.dto.ReservationCreateDTO;
import com.zenithmind.library.pojo.dto.ReservationUpdateDTO;
import com.zenithmind.library.pojo.query.ReservationQuery;
import com.zenithmind.library.pojo.vo.ReservationVO;
import com.zenithmind.library.service.ReservationService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 预约服务实现类
 * 
 * @author ZenithMind
 * @since 2024-06-14
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ReservationServiceImpl extends ServiceImpl<ReservationMapper, Reservation> implements ReservationService {

    private final ReservationMapper reservationMapper;
    private final BookMapper bookMapper;
    private final RedissonClient redissonClient;

    @Value("${library.reservation.max-per-user:3}")
    private Integer maxReservationsPerUser;

    @Value("${library.reservation.default-days:7}")
    private Integer defaultReservationDays;

    @Value("${library.reservation.max-days:30}")
    private Integer maxReservationDays;

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = "reservations", allEntries = true)
    public Result<ReservationVO> createReservation(ReservationCreateDTO createDTO) {
        log.info("创建预约，数据：{}", createDTO);

        String lockKey = "reservation:book:" + createDTO.getBookId() + ":" + createDTO.getUserId();
        RLock lock = redissonClient.getLock(lockKey);

        try {
            // 获取分布式锁，防止并发预约
            if (!lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                return Result.fail(ResultCode.FAIL.getCode(), "系统繁忙，请稍后重试", null);
            }

            // 检查用户是否可以预约
            Result<Boolean> canReserveResult = canUserReserve(createDTO.getUserId(), createDTO.getBookId());
            if (!canReserveResult.isSuccess()) {
                return Result.fail(canReserveResult.getCode(), canReserveResult.getMsg(), null);
            }

            // 检查图书是否可以预约
            Result<Boolean> canBookBeReservedResult = canBookBeReserved(createDTO.getBookId());
            if (!canBookBeReservedResult.isSuccess()) {
                return Result.fail(canBookBeReservedResult.getCode(), canBookBeReservedResult.getMsg(), null);
            }

            // 创建预约记录
            Reservation reservation = createReservationRecord(createDTO);
            boolean saved = save(reservation);
            if (!saved) {
                return Result.fail(ResultCode.FAIL.getCode(), "创建预约失败", null);
            }

            // 转换为VO并返回
            ReservationVO reservationVO = convertToVO(reservation);
            return Result.success(reservationVO);

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return Result.fail(ResultCode.INTERNAL_SERVER_ERROR.getCode(), "系统异常，请稍后重试", null);
        } catch (Exception e) {
            log.error("创建预约失败", e);
            return Result.fail(ResultCode.FAIL.getCode(), "创建预约失败：" + e.getMessage(), null);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = "reservations", allEntries = true)
    public Result<ReservationVO> updateReservation(String id, ReservationUpdateDTO updateDTO) {
        log.info("更新预约，ID：{}，数据：{}", id, updateDTO);

        // 检查预约是否存在
        Reservation existingReservation = getById(id);
        if (existingReservation == null) {
            return Result.fail(ResultCode.NOT_FOUND.getCode(), "预约记录不存在", null);
        }

        // 更新预约信息
        Reservation reservation = new Reservation();
        BeanUtils.copyProperties(updateDTO, reservation);
        reservation.setId(id);
        reservation.setUpdateTime(LocalDateTime.now());

        boolean updated = updateById(reservation);
        if (!updated) {
            return Result.fail(ResultCode.FAIL.getCode(), "更新预约失败", null);
        }

        // 返回更新后的预约信息
        Reservation updatedReservation = getById(id);
        ReservationVO reservationVO = convertToVO(updatedReservation);
        return Result.success(reservationVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = "reservations", allEntries = true)
    public Result<Void> cancelReservation(String id, String reason) {
        log.info("取消预约，ID：{}，原因：{}", id, reason);

        // 查询预约记录
        Reservation reservation = getById(id);
        if (reservation == null) {
            return Result.fail(ResultCode.NOT_FOUND.getCode(), "预约记录不存在", null);
        }

        // 检查预约状态
        if (reservation.getStatus() == null || 
            reservation.getStatus() == ReservationStatus.CANCELLED.getCode() ||
            reservation.getStatus() == ReservationStatus.FULFILLED.getCode()) {
            return Result.fail(ResultCode.PRECONDITION_FAILED.getCode(), "该预约无法取消", null);
        }

        // 更新预约状态
        reservation.setStatus(ReservationStatus.CANCELLED.getCode());
        reservation.setCancellationReason(reason);
        reservation.setCancellationTime(LocalDateTime.now());
        reservation.setUpdateTime(LocalDateTime.now());

        boolean updated = updateById(reservation);
        if (!updated) {
            return Result.fail(ResultCode.FAIL.getCode(), "取消预约失败", null);
        }

        // 更新排队位置
        reservationMapper.updateQueuePositions(reservation.getBookId(), reservation.getQueuePosition());

        return Result.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = "reservations", allEntries = true)
    public Result<Void> confirmPickup(String id) {
        log.info("确认取书，预约ID：{}", id);

        // 查询预约记录
        Reservation reservation = getById(id);
        if (reservation == null) {
            return Result.fail(ResultCode.NOT_FOUND.getCode(), "预约记录不存在", null);
        }

        // 检查预约状态
        if (reservation.getStatus() == null || 
            reservation.getStatus() != ReservationStatus.AVAILABLE.getCode()) {
            return Result.fail(ResultCode.PRECONDITION_FAILED.getCode(), "该预约不可取书", null);
        }

        // 更新预约状态
        reservation.setStatus(ReservationStatus.FULFILLED.getCode());
        reservation.setPickupDate(LocalDate.now());
        reservation.setUpdateTime(LocalDateTime.now());

        boolean updated = updateById(reservation);
        if (!updated) {
            return Result.fail(ResultCode.FAIL.getCode(), "确认取书失败", null);
        }

        return Result.success();
    }

    @Override
    @Cacheable(value = "reservations", key = "'page:' + #query.hashCode()")
    public PageResult<ReservationVO> getReservationPage(ReservationQuery query) {
        log.info("分页查询预约记录，查询条件：{}", query);

        Page<ReservationVO> page = query.toPage();
        IPage<ReservationVO> result = reservationMapper.selectReservationPage(page, query);

        return PageResult.of(result.getRecords(), result.getTotal(), result.getCurrent(), result.getSize());
    }

    @Override
    @Cacheable(value = "reservations", key = "'user:' + #userId + ':' + #query.hashCode()")
    public PageResult<ReservationVO> getReservationsByUser(String userId, ReservationQuery query) {
        log.info("根据用户查询预约记录，用户ID：{}，查询条件：{}", userId, query);

        Page<ReservationVO> page = query.toPage();
        IPage<ReservationVO> result = reservationMapper.selectReservationsByUser(page, userId, query);

        return PageResult.of(result.getRecords(), result.getTotal(), result.getCurrent(), result.getSize());
    }

    @Override
    @Cacheable(value = "reservations", key = "'book:' + #bookId + ':' + #query.hashCode()")
    public PageResult<ReservationVO> getReservationsByBook(String bookId, ReservationQuery query) {
        log.info("根据图书查询预约记录，图书ID：{}，查询条件：{}", bookId, query);

        Page<ReservationVO> page = query.toPage();
        IPage<ReservationVO> result = reservationMapper.selectReservationsByBook(page, bookId, query);

        return PageResult.of(result.getRecords(), result.getTotal(), result.getCurrent(), result.getSize());
    }

    @Override
    @Cacheable(value = "reservations", key = "'current:' + #userId")
    public List<ReservationVO> getCurrentReservationsByUser(String userId) {
        log.info("获取用户当前预约记录，用户ID：{}", userId);
        return reservationMapper.selectCurrentReservationsByUser(userId);
    }

    @Override
    @Cacheable(value = "reservations", key = "'queue:' + #bookId")
    public List<ReservationVO> getBookReservationQueue(String bookId) {
        log.info("获取图书预约队列，图书ID：{}", bookId);
        return reservationMapper.selectBookReservationQueue(bookId);
    }

    @Override
    public PageResult<ReservationVO> getAvailableReservations(ReservationQuery query) {
        log.info("获取可取书的预约记录，查询条件：{}", query);

        Page<ReservationVO> page = query.toPage();
        IPage<ReservationVO> result = reservationMapper.selectAvailableReservations(page, query);

        return PageResult.of(result.getRecords(), result.getTotal(), result.getCurrent(), result.getSize());
    }

    @Override
    public PageResult<ReservationVO> getExpiredReservations(ReservationQuery query) {
        log.info("获取过期的预约记录，查询条件：{}", query);

        Page<ReservationVO> page = query.toPage();
        IPage<ReservationVO> result = reservationMapper.selectExpiredReservations(page, query);

        return PageResult.of(result.getRecords(), result.getTotal(), result.getCurrent(), result.getSize());
    }

    @Override
    public Result<Boolean> canUserReserve(String userId, String bookId) {
        // 检查用户当前预约数量
        int currentReservations = reservationMapper.countUserCurrentReservations(userId);
        if (currentReservations >= maxReservationsPerUser) {
            return Result.fail(ResultCode.PRECONDITION_FAILED.getCode(), "预约数量已达上限（" + maxReservationsPerUser + "本）", null);
        }

        // 检查用户是否已预约该图书
        int userBookReservations = reservationMapper.countUserBookReservation(userId, bookId);
        if (userBookReservations > 0) {
            return Result.fail(ResultCode.CONFLICT.getCode(), "您已预约该图书", null);
        }

        // TODO: 检查用户是否已借阅该图书
        // int userBookBorrows = borrowRecordMapper.countUserCurrentBorrowByBook(userId, bookId);
        // if (userBookBorrows > 0) {
        //     return Result.error("您已借阅该图书，无需预约");
        // }

        return Result.success(true);
    }

    @Override
    public Result<Boolean> canBookBeReserved(String bookId) {
        Book book = bookMapper.selectById(bookId);
        if (book == null) {
            return Result.fail(ResultCode.NOT_FOUND.getCode(), "图书不存在", null);
        }

        if (book.getStatus() == null || book.getStatus() != 1) {
            return Result.fail(ResultCode.PRECONDITION_FAILED.getCode(), "图书已下架", null);
        }

        // 如果有可借阅副本，则不需要预约
        if (book.getAvailableCopies() != null && book.getAvailableCopies() > 0) {
            return Result.fail(ResultCode.PRECONDITION_FAILED.getCode(), "图书有可借阅副本，请直接借阅", null);
        }

        return Result.success(true);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = "reservations", allEntries = true)
    public Result<Void> processBookReturnNotification(String bookId) {
        log.info("处理图书归还后的预约通知，图书ID：{}", bookId);

        // 获取该图书的第一个预约记录
        List<ReservationVO> queue = reservationMapper.selectBookReservationQueue(bookId);
        if (queue.isEmpty()) {
            return Result.success();
        }

        ReservationVO firstReservation = queue.get(0);
        
        // 更新预约状态为可取书
        Reservation reservation = new Reservation();
        reservation.setId(firstReservation.getId());
        reservation.setStatus(ReservationStatus.AVAILABLE.getCode());
        reservation.setAvailableDate(LocalDate.now());
        reservation.setUpdateTime(LocalDateTime.now());

        boolean updated = updateById(reservation);
        if (!updated) {
            return Result.fail(ResultCode.FAIL.getCode(), "更新预约状态失败", null);
        }

        // 发送通知
        sendReservationNotification(firstReservation.getId());

        return Result.success();
    }

    @Override
    public Result<Void> sendReservationNotification(String reservationId) {
        log.info("发送预约通知，预约ID：{}", reservationId);
        
        // TODO: 实现通知发送逻辑
        // 1. 获取预约信息
        // 2. 获取用户联系方式
        // 3. 根据通知方式发送通知（邮件、短信、站内信）
        // 4. 更新通知状态
        
        reservationMapper.updateNotificationStatus(reservationId, 1);
        
        return Result.success();
    }

    @Override
    public Result<Integer> batchSendReservationNotifications() {
        log.info("批量发送预约通知");
        
        // 获取需要通知的预约记录
        List<ReservationVO> reservations = reservationMapper.selectReservationsNeedNotification();
        
        int count = 0;
        for (ReservationVO reservation : reservations) {
            Result<Void> result = sendReservationNotification(reservation.getId());
            if (result.isSuccess()) {
                count++;
            }
        }
        
        return Result.success(count);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Integer> autoProcessExpiredReservations() {
        log.info("自动处理过期预约");
        
        int count = reservationMapper.batchUpdateExpiredStatus();
        
        return Result.success(count);
    }

    @Override
    public ReservationVO getReservationStatistics(LocalDate startDate, LocalDate endDate) {
        log.info("获取预约统计信息，开始日期：{}，结束日期：{}", startDate, endDate);
        return reservationMapper.selectReservationStatistics(startDate, endDate);
    }

    @Override
    public List<ReservationVO> getHotReservedBooks(LocalDate startDate, LocalDate endDate, Integer limit) {
        log.info("获取热门预约图书，开始日期：{}，结束日期：{}，限制数量：{}", startDate, endDate, limit);
        return reservationMapper.selectHotReservedBooks(startDate, endDate, limit);
    }

    @Override
    public Result<String> exportReservations(ReservationQuery query) {
        log.info("导出预约记录，查询条件：{}", query);
        
        // TODO: 实现预约记录导出逻辑
        return Result.success("/exports/reservations_" + System.currentTimeMillis() + ".xlsx");
    }

    /**
     * 创建预约记录
     */
    private Reservation createReservationRecord(ReservationCreateDTO createDTO) {
        Reservation reservation = new Reservation();
        BeanUtils.copyProperties(createDTO, reservation);
        
        reservation.setReservationDate(LocalDate.now());
        
        // 设置到期日期
        LocalDate expirationDate = createDTO.getExpirationDate();
        if (expirationDate == null) {
            expirationDate = LocalDate.now().plusDays(defaultReservationDays);
        }
        reservation.setExpirationDate(expirationDate);
        
        // 设置排队位置
        int queuePosition = reservationMapper.getNextQueuePosition(createDTO.getBookId());
        reservation.setQueuePosition(queuePosition);
        
        reservation.setStatus(ReservationStatus.PENDING.getCode());
        reservation.setNotified(0);
        reservation.setCreateTime(LocalDateTime.now());
        reservation.setUpdateTime(LocalDateTime.now());
        
        return reservation;
    }

    /**
     * 转换为VO对象
     */
    private ReservationVO convertToVO(Reservation reservation) {
        // TODO: 实现完整的转换逻辑，包括关联数据
        ReservationVO vo = new ReservationVO();
        BeanUtils.copyProperties(reservation, vo);
        return vo;
    }
}
